import { Injectable } from '@angular/core';
import { FormPropertyChangeObject } from '../entity/property-change-entity';
import { set } from 'lodash-es';
import { DesignViewModelService, RefreshFormService, DomService, FormBindingType, SchemaService } from '@farris/designer-services';
import { DgControl } from '../utils/dg-control';
import { SchemaDOMMapping } from './schema-dom-mapping';
import { lowerFirst } from 'lodash-es';

/**
 * 控件属性变更后，同步DOM结构
 */
@Injectable()
export class ControlPropertyChangedService {

  constructor(
    private domService: DomService,
    private schemaService: SchemaService,
    private dgViewModelService: DesignViewModelService,
    private refreshFormService: RefreshFormService) { }

  afterPropertyChanged(
    changeObject: FormPropertyChangeObject,
    propertyData: any,
    parameters: any,
    focusedComponentInstance: any) {
    if (!focusedComponentInstance) {
      return;
    }
    const viewModelId = focusedComponentInstance.viewModelId;

    // 保存新增的变量
    this.addNewVariableToViewModel(changeObject, viewModelId);

    // 表达式相关属性：需要单独更新DOM结构
    this.updateExpressionAfterPropChange(propertyData.id, changeObject, parameters);

    // 收集关联属性(用于dgViewModel的变更)
    const changes = this.relateChangeObjects(changeObject);


    // 更新dgViewModel
    this.changeDgViewModel(propertyData, changes, viewModelId);

    // 通知控件应用变更
    if (focusedComponentInstance && focusedComponentInstance.onPropertyChanged) {
      focusedComponentInstance.onPropertyChanged(changeObject);
    }

    // 某些属性更新后需要重新刷新表单和控件树
    this.refreshFormAfterPropChanged(changeObject);
  }

  /**
   * 新版属性编辑器，在编辑过程中可能会新增变量，此处需要将新增的变量追加到ViewModel中
   */
  private addNewVariableToViewModel(changeObject: FormPropertyChangeObject, viewModelId: string) {
    const newPropertyValue = changeObject.propertyValue;
    if (newPropertyValue && newPropertyValue.isNewVariable && typeof newPropertyValue === 'object' &&
      newPropertyValue.type === 'Variable') {
      // 如果有则加入新变量
      delete newPropertyValue.isNewVariable;
      const newVar = {
        id: newPropertyValue.field,
        category: 'locale',
        code: newPropertyValue.path,
        name: newPropertyValue.path,
        type: newPropertyValue.newVariableType || 'String'
      };
      delete newPropertyValue.newVariableType;
      const viewModel = this.domService.getViewModelById(viewModelId);
      if (!viewModel.states.find(s => s.id === newVar.id)) {
        viewModel.states.push(newVar);
      }
    }
  }

  /**
   * 收集关联属性的变更(用于dgViewModel的变更)
   * @param changeObject 变更集
   */
  private relateChangeObjects(changeObject: FormPropertyChangeObject) {
    const changes: any[] = [changeObject];
    if (changeObject.relateChangeProps && changeObject.relateChangeProps.length) {
      changes.push(...changeObject.relateChangeProps);
    }

    if (changeObject.categoryId && changeObject.categoryId.includes('gridFieldEditor')) {
      changes.forEach(change => {
        Object.assign(change, {
          categoryId: changeObject.categoryId,
          propertyPath: changeObject.propertyPath
        });
      });
    }

    return changes;
  }

  /**
   * 属性面板更新后同步DOM结构
   * @param controlId 控件ID
   * @param changeObject 变更集
   */
  private updateExpressionAfterPropChange(controlId: string, changeObject: FormPropertyChangeObject, parameters: any): any {

    if (changeObject.categoryId === 'expressionData') {
      const domJson = this.domService.domDgMap.get(controlId);
      this.updateExpressionDomAfterPropChange(domJson, changeObject, parameters);
    }

  }

  /**
   * 更新表达式相关dom：帮助前表达式需要额外记录ID
   * @param domJson 控件DOM结构
   * @param change 变更集
   * @param parameters 表达式ID
   */
  private updateExpressionDomAfterPropChange(domJson: any, change: any, parameters: any) {
    if (change.categoryId !== 'expressionData' || change.propertyID !== 'dataPicking') {
      return;
    }

    if (!change.propertyPath) {
      domJson.lookupPickingExpression = parameters;
      return;
    }

    set(domJson, change.propertyPath + '.lookupPickingExpression', parameters);
  }


  /**
   *  收集Schema字段的变更
   * @param propertyData 属性值
   * @param changeObjects 变更集
   * @param viewModelId  VMID
   */
  private changeDgViewModel(propertyData: any, changeObjects: FormPropertyChangeObject[], viewModelId: string) {

    const dgVM = this.dgViewModelService.getDgViewModel(viewModelId); // 当前VM
    if (!dgVM) {
      return;
    }
    changeObjects.map(changeObject => {
      switch (propertyData.type) {
        case DgControl.FieldSet.type: {
          // 分组节点修改标题后需要同步ViewModel字段的分组
          if (changeObject.propertyID === 'title') {
            dgVM.changeGroupName(propertyData.id, changeObject.propertyValue);
          }
          break;
        }
        default: {
          // 控件节点
          let dgVMField;
          if (propertyData.binding && propertyData.binding.type === FormBindingType.Form && propertyData.binding.field) {
            dgVMField = dgVM.fields.find(f => f.id === propertyData.binding.field);
          }
          if (dgVMField) {
            const dgVMChange = this.getSchemaChangeByDomChange(propertyData, changeObject);
            dgVM.changeField(dgVMField.id, dgVMChange);
          }
        }
      }
    });
  }

  /**
   * 更新DOM的修改至Schema实体
   * @param propertyData 属性值
   * @param changeObject 变更集
   */
  getSchemaChangeByDomChange(propertyData, changeObject: FormPropertyChangeObject) {
    const schemaChange: any = {};

    let mappingArray = [];
    if (changeObject.categoryId && (changeObject.categoryId.includes('gridFieldEditor') || changeObject.categoryId.includes('tableTdEditor'))) {  // 列编辑器属性
      mappingArray = SchemaDOMMapping.mappingDomPropAndSchemaProp(propertyData.editor);
    } else if (!changeObject.propertyPath) {
      mappingArray = SchemaDOMMapping.mappingDomPropAndSchemaProp(propertyData);
    }
    const mappingEntity = mappingArray.find(f => f.domField === changeObject.propertyID);
    if (!mappingEntity) {
      return {};
    }
    // 只读、必填属性：只有在设置为布尔值时才更新到schema，设置为状态机、变量、表达式时不更新
    if (changeObject.propertyID === 'readonly' || changeObject.propertyID === 'require') {
      if (typeof (changeObject.propertyValue) !== 'boolean') {
        return schemaChange;
      }
    }
    const shemaFieldPath = mappingEntity.schemaField;
    set(schemaChange, shemaFieldPath, changeObject.propertyValue);
    return schemaChange;
  }

  /**
   * 某些属性更新后需要重新刷新表单和控件树
   * @param propertyType 控件类型
   * @param changeObject 属性变更
   */
  refreshFormAfterPropChanged(changeObject: FormPropertyChangeObject) {
    // 表单整体刷新
    if (changeObject.needRefreshForm) {
      this.refreshFormService.refreshFormDesigner.next();
    }
    // 表单局部刷新
    if (changeObject.needRefreshedComponentId) {
      this.refreshFormService.refreshFormDesigner.next(changeObject.needRefreshedComponentId);
    }
  }

  /**
   * 属性变更后，将属性同步到Schema字段上，目前用于零代码表单驱动
   * @param changeObject 变更集
   * @param propertyData 控件schema
   * @param viewModelId 模型id
   */
  syncSchemaFieldAfterControlPropChanged(changeObject: FormPropertyChangeObject, propertyData: any, viewModelId: string) {
    if (!changeObject.needSyncToSchemaField || !propertyData.binding || propertyData.binding.type !== FormBindingType.Form) {
      return;
    }

    const fieldId = propertyData.binding.field;
    if (!this.schemaService.addedFieldsByControlBox || !this.schemaService.addedFieldsByControlBox.some(id => id === fieldId)) {
      return;
    }

    // 设计时字段
    const dgvm = this.dgViewModelService.getDgViewModel(viewModelId);
    const dgField = dgvm.fields.find(f => f.id === fieldId);

    // schema字段
    const schemaField = this.schemaService.getFieldByID(fieldId);
    if (!schemaField || !dgField) {
      return;
    }

    switch (changeObject.propertyID) {
      case 'title': {
        schemaField.name = changeObject.propertyValue;
        break;
      }
      case 'bindingCode': {
        let rule = /^[a-z][a-zA-Z0-9]{0,14}$/;
        const bindingCode = lowerFirst(changeObject.propertyValue);
        if (rule.test(bindingCode)) {
          schemaField.bindingField = bindingCode;
          schemaField.bindingPath = bindingCode;
          schemaField.code = bindingCode;
          schemaField.label = bindingCode;
          schemaField.path = bindingCode;

          dgField.bindingField = bindingCode;
          dgField.bindingPath = bindingCode;
          dgField.code = bindingCode;
          dgField.label = bindingCode;
          dgField.path = bindingCode;

          propertyData.binding.fullPath = bindingCode;
          propertyData.path = bindingCode;
          propertyData.binding.path = bindingCode;
        }
        break;
      }
    }


  }

}
